/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.engine.cmd.task;

import cn.easyplatform.contexts.ListContext;
import cn.easyplatform.contexts.WorkflowContext;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.dos.LogDo;
import cn.easyplatform.dos.Record;
import cn.easyplatform.engine.runtime.PageTaskSupport;
import cn.easyplatform.engine.runtime.RuntimeTask;
import cn.easyplatform.engine.runtime.RuntimeTaskFactory;
import cn.easyplatform.engine.runtime.datalist.DataListUtils;
import cn.easyplatform.engine.runtime.page.PageTask;
import cn.easyplatform.entities.BaseEntity;
import cn.easyplatform.entities.beans.task.DecisionBean;
import cn.easyplatform.entities.beans.task.TaskBean;
import cn.easyplatform.entities.beans.task.TransitionBean;
import cn.easyplatform.entities.helper.EventLogic;
import cn.easyplatform.i18n.I18N;
import cn.easyplatform.interceptor.AbstractCommand;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Nums;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.AbstractResponseMessage;
import cn.easyplatform.messages.request.BeginRequestMessage;
import cn.easyplatform.messages.request.ListBatchRequestMessage;
import cn.easyplatform.messages.request.NextRequestMessage;
import cn.easyplatform.messages.response.PageResponseMessage;
import cn.easyplatform.messages.response.SimpleResponseMessage;
import cn.easyplatform.messages.response.TaskNextErrorResponseMessage;
import cn.easyplatform.messages.vos.NextVo;
import cn.easyplatform.messages.vos.PageVo;
import cn.easyplatform.messages.vos.TaskVo;
import cn.easyplatform.messages.vos.datalist.ListBatchVo;
import cn.easyplatform.messages.vos.datalist.ListNextVo;
import cn.easyplatform.support.scripting.BreakPoint;
import cn.easyplatform.type.Constants;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.util.EntityUtils;
import cn.easyplatform.util.MessageUtils;
import cn.easyplatform.util.RuntimeUtils;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class NextCmd extends AbstractCommand<NextRequestMessage> {

    /**
     * @param req
     */
    public NextCmd(NextRequestMessage req) {
        super(req);
    }

    @Override
    public IResponseMessage<?> execute(CommandContext cc) {
        WorkflowContext ctx = cc.getWorkflowContext();
        if (ctx == null)
            return new SimpleResponseMessage();
        if (req.getActionFlag() < 0) {
            cc.setBreakPoint(null);
            String name = ctx.getParameterAsString("808");
            if (name.equals(Constants.ON_INIT)
                    || name.equals(Constants.ON_LOAD))
                cc.getWorkflowContext().prev(cc);
            return new SimpleResponseMessage();
        }
        BreakPoint bp = cc.getBreakPoint();
        NextVo nv = req.getBody();
        if (nv instanceof ListNextVo) {// 来自列表
            ListNextVo vo = (ListNextVo) req.getBody();
            if (bp == null) {
                if (vo.getKeys() != null) {
                    ListContext lc = ctx.getList(vo.getId());
                    if (lc.isCustom()) {
                        FieldDo[] data = DataListUtils.getRow(cc, lc,
                                vo.getKeys());
                        Record record = new Record();
                        for (int i = 0; i < data.length; i++)
                            record.set(data[i]);
                        ctx.setData(record);
                    } else {
                        lc.lock(cc, vo.getKeys());
                        ctx.setData(DataListUtils.getRecord(cc, lc,
                                vo.getKeys()));
                    }
                    ctx.setParameter("814", "R");
                    ctx.setParameter("833", lc.getBean().getTable());
                    ctx.setParameter("854", true);
                }
            } else
                nv.setData(null);
        }
        if (nv.getData() != null
                && ctx.getParameterAsString("830").equals(
                EntityType.PAGE.getName())) {
            // 来自页面
            PageTaskSupport support = (PageTaskSupport) RuntimeTaskFactory
                    .createRuntime(EntityType.PAGE.getName());
            IResponseMessage<?> resp = null;
            if (bp == null)
                resp = support.doNext(cc, nv.getData());
            else
                resp = support.doNextBreakPoint(cc, bp);
            if (resp != null) {
                // 如果是不显示的功能，需要返回到之前有显示的页面
                if (!ctx.getParameterAsBoolean("816")) {
                    if (ctx.prev(cc)) {
                        return new TaskNextErrorResponseMessage(
                                support.doPrev(cc), resp);
                    } else {
                        cc.removeWorkflowContext();
                        return new TaskNextErrorResponseMessage(
                                new SimpleResponseMessage(), resp);
                    }
                }
                return resp;
            }
            bp = null;
        }
        if (bp == null) {
            // 用OPEN_EMBBED打开的功能由主功能管理
            //int om = ctx.getParameterAsInt("805");
            //if (om == Constants.OPEN_EMBBED)
            //    return new SimpleResponseMessage();
            DecisionBean db = ctx.getAs(DecisionBean.class, "817");
            if (db != null)
                return doNext(cc, ctx, db, nv.getCode());
            else
                return doCommit(cc, ctx);
        } else {
            PageTaskSupport support = new PageTask();
            IResponseMessage<?> resp = support.doPageBreakPoint(cc, bp);
            if (!resp.isSuccess()) {
                char c = resp.getCode().toLowerCase().charAt(0);
                if (c != 'c' && c != 'w')
                    ctx.prev(cc);
            } else
                ctx.store();
            return resp;
        }
    }

    private IResponseMessage<?> doCommit(CommandContext cc, WorkflowContext ctx) {
        ctx.commit(cc);
        if (cc.getUser().getLogLevel() >= LogDo.LEVEL_TASK)
            RuntimeUtils.log(cc, LogDo.TYPE_TASK, "commit",
                    ctx.getParameterAsString("801"));
        char rt = ctx.getParameterAsChar("804");
        if (rt == '1') {
            // 重新执行功能
            TaskVo tv = new TaskVo(ctx.getParameterAsString("801"));
            ListBatchVo pendingTasks = ctx.getPendingList();
            ListContext lc = null;
            if (pendingTasks != null && !pendingTasks.getKeys().isEmpty())
                lc = ctx.getList(1, pendingTasks.getId());
            cc.removeWorkflowContext();
            IResponseMessage<?> resp = new BeginCmd(new BeginRequestMessage(tv))
                    .execute(cc);
            ctx = cc.getWorkflowContext();
            if (resp.isSuccess() && lc != null) {
                ctx.setList(lc);
                BatchCmd cmd = new BatchCmd(new ListBatchRequestMessage(
                        ctx.getId(), pendingTasks));
                return cmd.execute(cc);
            }
            // 增加标识，以便客户端辩别及后继的处理，参考TaskSupport.next
            ((AbstractResponseMessage) resp).setRedo(true);
            return resp;
        } else if (rt == '2') {
            // 回到第一个功能
            ctx.reset(cc);
            if (ctx.getPendingList() != null
                    && !ctx.getPendingList().getKeys().isEmpty()) {
                // 重新回到第二个功能对未处理的列表记录进行修改
                BatchCmd cmd = new BatchCmd(new ListBatchRequestMessage(
                        ctx.getId(), ctx.getPendingList()));
                return cmd.execute(cc);
            } else {
                ctx.setPendingList(null);
                ctx.setParameter("759", "Prev");
                RuntimeTask support = RuntimeTaskFactory.createRuntime(ctx
                        .getParameterAsString("830"));
                return support.doPrev(cc);
            }
        } else {
            cc.removeWorkflowContext();
            return new SimpleResponseMessage();
        }
    }

    private IResponseMessage<?> doNext(CommandContext cc, WorkflowContext ctx,
                                       DecisionBean db, String code) {
        TransitionBean transition = null;
        if (Strings.isBlank(db.getExpr())) {
            if (db.getTransitions() == null || db.getTransitions().isEmpty())
                return doCommit(cc, ctx);
            else
                transition = db.getTransitions().get(0);
        } else {
            String name = (String) RuntimeUtils.evalExpr(cc, db.getExpr(),
                    ctx.getRecord());
            if (Strings.isBlank(name))
                return doCommit(cc, ctx);
            for (TransitionBean tb : db.getTransitions()) {
                if (tb.getName().equals(name)) {
                    transition = tb;
                    break;
                }
            }
            if (transition == null)
                return doCommit(cc, ctx);
        }
        String taskId = transition.getTo().trim();
        if (taskId.charAt(0) == '$')
            taskId = (String) ctx.getRecord().getValue(taskId.substring(1));
        TaskBean task = cc.getEntity(taskId);
        if (task == null)
            return MessageUtils.entityNotFound(EntityType.TASK.getName(),
                    taskId);
        task = EntityUtils.getInheritTask(cc, ctx.getRecord(), task);
        String refId = task.getRefId();
        if (!Strings.isBlank(refId) && refId.startsWith("$")
                && cc.getWorkflowContext() != null)
            refId = (String) cc.getWorkflowContext().getRecord()
                    .getValue(refId.substring(1));
        if (refId == null)
            return MessageUtils.entityNotFound(EntityType.PAGE.getName(),
                    task.getRefId());
        if (refId.startsWith("~./")) {// 表示自定义页面
            cc.setWorkflowContext(ctx);
            PageVo pv = new PageVo();
            pv.setId(ctx.getId());
            pv.setPage(refId);
            pv.setTitile(ctx.getParameterAsString("811"));
            pv.setImage(ctx.getParameterAsString("813"));
            pv.setOpenModel(ctx.getParameterAsInt("805"));
            pv.setProcessCode(ctx.getParameterAsString("814"));
            pv.setVisible(ctx.getParameterAsBoolean("816"));
            ctx.store();
            return new PageResponseMessage(pv);
        }
        BaseEntity base = cc.getEntity(refId);
        if (base == null)
            return MessageUtils.entityNotFound(EntityType.PAGE.getName(),
                    task.getRefId());
        RuntimeTask runtime = RuntimeTaskFactory.createRuntime(base.getType());
        EventLogic el = null;
        if (!Strings.isBlank(transition.getLogicId())) {
            el = new EventLogic();
            el.setId(transition.getLogicId());
        } else if (!Strings.isBlank(transition.getMappings())) {
            el = new EventLogic();
            el.setContent(transition.getMappings());
        }
        task = task.clone();
        if (el != null) {
            task.setOnInit(el);
            if (!Strings.isBlank(code))
                task.setProcessCode(code);
        } else if (!Strings.isBlank(code)) {
            task.setProcessCode(code);
        }
        task.setName(RuntimeUtils.getLabel(cc, task.getName()));
        String layer = transition.getFrom();
        if (!Strings.isBlank(layer)) {
            if (layer.startsWith("$")) {
                Object obj = ctx.getRecord().getValue(layer.substring(1));
                if (obj != null)
                    layer = obj.toString();
            }
            if (!ctx.next(Nums.toInt(layer, -1), task))
                return new SimpleResponseMessage("E017", I18N.getLabel(
                        "context.from.not.found", layer));
        } else
            ctx.next(-1, task);
        IResponseMessage<?> resp = runtime.doTask(cc, base);
        if (resp.isSuccess())
            ctx.store();
        else {
            char c = resp.getCode().toLowerCase().charAt(0);
            if (c != 'c' && c != 'w')
                ctx.prev(cc);
        }
        return resp;
    }

    @Override
    public String getName() {
        return "task.Next";
    }
}
